package com.tbynet.jwp.framework.core;

import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Db;
import com.tbynet.jwp.framework.exception.RepositoryException;

import java.util.List;
import java.util.function.Supplier;

/**
 * 命令仓储基类 - 封装通用写操作，统一异常处理
 * @param <M> 泛型：JwpModel子类，对应数据库表
 */
public abstract class JwpCommandRepository<M extends JwpModel<M>> {
    /**
     * 日志工具
     */
    protected final Log log = Log.getLog(this.getClass());
    /**
     * 当前仓储对应的Model类
     */
    protected final Class<M> modelClass;

    /**
     * 构造器：初始化Model类
     * @param modelClass JwpModel的Class对象
     */
    public JwpCommandRepository(Class<M> modelClass) {
        this.modelClass = modelClass;
    }

    // -------------------------- 异常包装模板方法 --------------------------
    /**
     * 写操作异常包装模板：捕获数据库异常，包装为RepositoryException抛出
     * @param action 写操作逻辑
     * @param errorMsg 异常提示信息
     * @param <T> 操作返回值类型
     * @return 操作结果
     */
    protected <T> T executeWriteOperation(Supplier<T> action, String errorMsg) {
        try {
            return action.get();
        } catch (Exception e) {
            log.error(String.format("%s，异常详情：", errorMsg), e);
            // 抛出统一的仓储层异常，由Service层捕获处理
            throw new RepositoryException(errorMsg, e);
        }
    }

    // -------------------------- 通用写操作方法 --------------------------
    /**
     * 单条数据保存（新增）
     * @param model 待保存的Model对象
     * @return 保存是否成功
     */
    public boolean save(M model) {
        return executeWriteOperation(() -> model.save(),
                String.format("保存%s数据失败，数据ID：%s", modelClass.getSimpleName(), model.getStr("id")));
    }

    /**
     * 单条数据更新
     * @param model 待更新的Model对象（需包含主键）
     * @return 更新是否成功
     */
    public boolean update(M model) {
        return executeWriteOperation(() -> model.update(),
                String.format("更新%s数据失败，数据ID：%s", modelClass.getSimpleName(), model.getStr("id")));
    }

    /**
     * 根据主键删除数据
     * @param id 主键ID
     * @return 删除是否成功
     */
    public boolean deleteById(Object id) {
        return executeWriteOperation(() -> Db.deleteById(getTableName(), id),
                String.format("删除%s数据失败，主键ID：%s", modelClass.getSimpleName(), id));
    }

    /**
     * 批量保存数据
     * @param models 待保存的Model列表
     * @return 成功保存的条数
     */
    public int batchSave(List<M> models) {
        if (models.isEmpty()) {
            log.warn("批量保存%s数据，传入的列表为空", modelClass.getSimpleName());
            return 0;
        }
        return executeWriteOperation(() -> {
            int successCount = 0;
            // 分批次批量保存，避免单次提交数据过多
            for (int i = 0; i < models.size(); i += JwpConstants.MAX_BATCH_OPERATION_SIZE) {
                int end = Math.min(i + JwpConstants.MAX_BATCH_OPERATION_SIZE, models.size());
                List<M> batch = models.subList(i, end);
                successCount += Db.batchSave(batch, batch.size()).length;
            }
            return successCount;
        }, String.format("批量保存%s数据失败，待保存条数：%s", modelClass.getSimpleName(), models.size()));
    }

    /**
     * 批量更新数据
     * @param models 待更新的Model列表（需包含主键）
     * @return 成功更新的条数
     */
    public int batchUpdate(List<M> models) {
        if (models.isEmpty()) {
            log.warn("批量更新%s数据，传入的列表为空", modelClass.getSimpleName());
            return 0;
        }
        return executeWriteOperation(() -> {
            int successCount = 0;
            for (int i = 0; i < models.size(); i += JwpConstants.MAX_BATCH_OPERATION_SIZE) {
                int end = Math.min(i + JwpConstants.MAX_BATCH_OPERATION_SIZE, models.size());
                List<M> batch = models.subList(i, end);
                successCount += Db.batchUpdate(batch, batch.size()).length;
            }
            return successCount;
        }, String.format("批量更新%s数据失败，待更新条数：%s", modelClass.getSimpleName(), models.size()));
    }

    /**
     * 自定义写操作（子类扩展）
     * 用于处理复杂的写操作（如批量删除、更新指定字段）
     * @param sql 自定义写SQL
     * @param params SQL参数
     * @return 受影响的行数
     */
    public int customUpdate(String sql, Object... params) {
        return executeWriteOperation(() -> Db.update(sql, params),
                String.format("执行自定义写SQL失败，SQL：%s，参数：%s", sql, params));
    }

    // -------------------------- 工具方法 --------------------------
    /**
     * 获取Model对应的数据库表名（基于JFinal的Model约定）
     * @return 表名
     */
    private String getTableName() {
        try {
            M model = modelClass.getDeclaredConstructor().newInstance();
            return model.getTableName();
        } catch (Exception e) {
            log.error("获取%s的表名失败", modelClass.getSimpleName(), e);
            throw new RepositoryException("获取表名失败", e);
        }
    }
}